package com.demo.webflux.spring5core.framework.context;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

import com.demo.webflux.spring5core.framework.annotation.Autowired;
import com.demo.webflux.spring5core.framework.annotation.Controller;
import com.demo.webflux.spring5core.framework.annotation.Service;
import com.demo.webflux.spring5core.framework.aop.AopConfig;
import com.demo.webflux.spring5core.framework.aop.AopProxy;
import com.demo.webflux.spring5core.framework.aop.JdkDynamicAopProxy;
import com.demo.webflux.spring5core.framework.aop.support.AdvisedSupport;
import com.demo.webflux.spring5core.framework.beans.BeanWrapper;
import com.demo.webflux.spring5core.framework.beans.config.BeanDefinition;
import com.demo.webflux.spring5core.framework.beans.config.BeanPostProcessor;
import com.demo.webflux.spring5core.framework.beans.support.BeanDefinitionReader;
import com.demo.webflux.spring5core.framework.context.support.DefaultListableBeanFactory;
import com.demo.webflux.spring5core.framework.core.BeanFactory;

/**直接接触用户的入口，主要实现DefaultListableBeanFactory里的refresh方法，和BeanFactory的getBean方法，完成ioc、di、aop的衔接
 * @author zhangsulei
 *
 */
public class ApplicationContext extends DefaultListableBeanFactory implements BeanFactory {

	private String[] configLocations;
	private BeanDefinitionReader reader;
	// 单例ioc容器缓存
	private Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>();
	// 存储所有被代理过的对象
	private Map<String, BeanWrapper> factoryBeanInstanceCache = new ConcurrentHashMap<>();

	public ApplicationContext(String... configLocations) {
		this.configLocations = configLocations;
		try {
			refresh();
		}catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public void refresh() throws Exception {
		// 1、定位配置文件
		reader = new BeanDefinitionReader(configLocations);
		// 2、加载配置文件，扫描相关的类，把它们封装成BeanDefinition
		List<BeanDefinition> beanDefinitions = reader.loadBeanDefinitions();
		// 3、注册，把配置信息放到容器里面(伪ioc容器)
		doRegisterBeanDefinition(beanDefinitions);
		// 4、把不是延时加载的类提前初始化
		doAutowrited();
	}

	/**
	 * 只处理非延迟加载情况
	 */
	private void doAutowrited() {
		for(Map.Entry<String, BeanDefinition> beanDefinitionEntry : super.beanDefinitionMap.entrySet()) {
			String beanName = beanDefinitionEntry.getKey();
			if(!beanDefinitionEntry.getValue().isLazyInit()) {
				try {
					getBean(beanName);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}

	private void doRegisterBeanDefinition(List<BeanDefinition> beanDefinitions) throws Exception {
		for (BeanDefinition beanDefinition : beanDefinitions) {
			if(super.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())) {
				throw new Exception("The '" + beanDefinition.getFactoryBeanName() + "' is exists!");
			}
			super.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(), beanDefinition);
		}
	}

	@Override
	public Object getBean(Class<?> beanClass) throws Exception {
		return getBean(beanClass.getName());
	}

	@Override
	public Object getBean(String beanName) throws Exception {
		BeanDefinition beanDefinition = super.beanDefinitionMap.get(beanName);

		try {
			// 生成通知事件
			BeanPostProcessor beanPostProcessor = new BeanPostProcessor();
			Object instance = instantiateBean(beanDefinition);
			if(null == instance) {
				return null;
			}
			// 在实例初始化以前调用一次
			beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
			BeanWrapper beanWrapper = new BeanWrapper(instance);
			factoryBeanInstanceCache.put(beanName, beanWrapper);
			// 在实例初始化之后调用一次
			beanPostProcessor.postProcessAfterInitialization(instance, beanName);
			populateBean(beanName, instance);

			return factoryBeanInstanceCache.get(beanName).getWrappedInstance();
		}catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	private void populateBean(String beanName, Object instance) throws Exception {
		Class clazz = instance.getClass();
		if(!(clazz.isAnnotationPresent(Controller.class) || clazz.isAnnotationPresent(Service.class))){
			return;
		}
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {
			if(!field.isAnnotationPresent(Autowired.class)) {
				continue;
			}
			Autowired autowired = field.getAnnotation(Autowired.class);

			String autowiredBeanName = autowired.value().trim();
			if("".equals(autowiredBeanName)) {
				autowiredBeanName = toLowerFirstCase(field.getType().getSimpleName());
			}
			field.setAccessible(true);
			try {
				BeanWrapper autowiredBeanWrapper = factoryBeanInstanceCache.get(autowiredBeanName);
				if(null == autowiredBeanWrapper) {
					field.set(instance, getBean(autowiredBeanName));
				}else {
					field.set(instance, autowiredBeanWrapper.getWrappedInstance());
				}
			} catch (IllegalArgumentException | IllegalAccessException e) {
				e.printStackTrace();
			}
		}
	}

	/**传入一个BeanDefinition，返回一个实例bean
	 * @param beanDefinition
	 * @return
	 */
	private Object instantiateBean(BeanDefinition beanDefinition) {
		Object instance = null;
		String className = beanDefinition.getBeanClassName();
		try {
			// 因为根据class才能确定一个类是否有实例
			if(factoryBeanObjectCache.containsKey(beanDefinition.getFactoryBeanName())) {
				instance = factoryBeanObjectCache.get(beanDefinition.getFactoryBeanName());
			}else {
				Class<?> clazz = Class.forName(className);
				instance = clazz.newInstance();

				AdvisedSupport config = instantionAopConfig(beanDefinition);
				config.setTargetClass(clazz);
				config.setTarget(instance);
				config.parse();

				if(config.pointCutMatch()) {
					AopProxy proxy = createProxy(config);
					if(proxy !=null) {
						instance = proxy.getProxy();
					}
				}

				factoryBeanObjectCache.put(beanDefinition.getFactoryBeanName(), instance);
			}
			return instance;
		}catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	private AdvisedSupport instantionAopConfig(BeanDefinition beanDefinition) throws Exception {
		AopConfig config = new AopConfig();
		config.setPointCut(reader.getConfig().getProperty("pointCut"));
		config.setAspectClass(reader.getConfig().getProperty("aspectClass"));
		config.setAspectBefore(reader.getConfig().getProperty("aspectBefore"));
		config.setAspectAfter(reader.getConfig().getProperty("aspectAfter"));
		config.setAspectAfterThrow(reader.getConfig().getProperty("aspectAfterThrow"));
		config.setAspectAfterThrowingName(reader.getConfig().getProperty("aspectAfterThrowingName"));

		return new AdvisedSupport(config);
	}

	private AopProxy createProxy(AdvisedSupport config) {
		Class targetClass = config.getTargetClass();
		if(targetClass.getInterfaces().length > 0) {
			return new JdkDynamicAopProxy(config);
		}
		// 应该是cglib实现的代理
		return null;
	}

	public String[] getBeanDefinitionNames() {
		return this.beanDefinitionMap.keySet().toArray(new String[this.beanDefinitionMap.size()]);
	}

	public int getBeanDefinitionCount() {
		return this.beanDefinitionMap.size();
	}

	public Properties getConfig() {
		return this.reader.getConfig();
	}

	/**类名首字母改为小写
	 * 为了简化逻辑，就不做其他判断了
	 * @param simpleName
	 * @return
	 */
	private String toLowerFirstCase(String simpleName) {
		char[] chars = simpleName.toCharArray();
		// 因为大小写字母的ascii码相差32
		// 而且大写字母的ascii码要小于小写字母的ascii码
		// 在java中，对char做算术运算，实际上就是对ascii码做算术运算
		chars[0] += 32;
		return String.valueOf(chars);
	}

}
